<template>
  <div v-if="source.data.id && source.data.cellData" class="OctopusItem">
    <!--<button v-on:click="jqtest()">JQTest</button>-->
    <!--<input id="sssdsdsdsds"/>-->
    <!--<input v-model.number="rowcount" type="number" min="0" max="100" step="0.01" />-->
    <div class="Operation" v-if="saved || backed || source.button">
      <el-button type="success" v-if="saved" v-on:click="save">Save</el-button>
      <!--<div class="warning Button" v-if="printed" v-on:click="print">Print</div>-->
      <el-button v-for="(button,buttonKey) in source.button" :key="buttonKey"
                 v-bind="{type: button.class}" v-on:click="event(button.click)">
        {{button.text}}
      </el-button>
      <el-button type="info" v-if="backed" v-on:click="back">Back</el-button>
    </div>
    <div class="Content">
      <!-- 如果使用了插槽 -->
      <!-- <template v-if="true">-->
      <!--        <div v-for="(cell,cellIndex) in source.data.cellData">-->
      <!--          <slot name="item" :data="{item:cell,cell:source.data.cellData}"></slot>-->
      <!--        </div>-->
      <!-- </template>-->
      <!-- 八爪鱼渲染 -->
      <template v-if="source.render==true || source.render==null">
        <div class="el-row">
          <div v-for="(cell,cellIndex) in source.data.cellData" v-if="cell.show"
               :key="cellIndex"
               v-bind:class="'el-col el-col-xs-' + cell.rasterizeXS*2 + ' el-col-sm-' + cell.rasterizeSM*2 + ' el-col-md-'
                       + cell.rasterizeMD*2 + ' el-col-lg-' + cell.rasterizeLG*2 + ' ' + cell.name">
            <div>{{cell.displayName}}</div>
            <o-control v-if="status" v-bind="{data:cell}" v-bind:changed="change" v-model="cell.defaultValue"></o-control>
          </div>
        </div>
      </template>
      <!-- 不渲染，交由前端样式自定义 -->
      <template v-else>
        <div class="Item" v-for="(cell,cellIndex) in source.data.cellData" v-if="cell.show">
          <div class="Body">
            <div class="DisplayName">{{cell.displayName}}</div>
            <o-control v-if="status" v-bind="{data:cell}" v-bind:changed="change" v-model="cell.defaultValue"></o-control>
          </div>
        </div>
      </template>

    </div>
  </div>
  <div v-else>Octopus Framework Initialization Exception.</div>
</template>

<script>
  export default {
    name: 'ItemEngine', /* 这个name暂时不知道用啥用，根据官方文档说的是方便排错的 */
    data() {
      return {
        octopus: this.$parent.octopus,
        status: true,
        dataExtend: null,
      }
    },
    created: function () {
      this.init();
      this.octopus.tips.debug("[" + this.source.data.id + "] ItemEngine执行了created事件。");
    },
    // 只会执行一次
    mounted: function () {
      // this.$nextTick(function () {
      //   this.octopus.tips.debug("[" + this.source.data.id + "] ItemEngine执行了$nextTick");
      // });
      // this.octopus.tips.debug("[" + this.source.data.id + "] ItemEngine执行了mounted事件。");
    },
    beforeUpdate: function () {
      // this.octopus.tips.debug("[" + this.source.data.id + "] ItemEngine执行了beforeUpdate事件。");
    },
    updated: function () {
      this.octopus.tips.debug("[" + this.source.data.id + "] ItemEngine执行了updated事件。");
    },
    // 活动事件，写在这里的好处是不管是第一次创建还是后退回来，都会触发这个事件
    // 比较典型的就是列表组件，返回的时候往往是需要刷新的。
    activated: function () {
      this.load();
      // this.$nextTick(function () {
      //   this.octopus.tips.debug("[" + this.source.data.id + "] ItemEngine执行了activated.$nextTick事件。");
      // });
      this.octopus.tips.debug("[" + this.source.data.id + "] ItemEngine执行了activated事件。");
    },
    // 组件停用时，也就是切换出去的时候，需要注意的是，Item组件切换出去，不允许缓存，需要释放到初始状态，以便于下次进来。
    deactivated: function () {
      this.destroyData();
      this.octopus.tips.debug("[" + this.source.data.id + "] ItemEngine执行了deactivated事件。");
    },
    computed: {
      // 如果cellData数据发生了改变,则触发这个事件。
      changeCell: function () {
        return this.source.id;
      },
      changePage: function () {

      }
    },
    methods: {
      // 执行加载
      load: function (type) {
        // 满足要求才执行查询
        if (this.source.data.id && this.source.data.cellData) {
          // 设置请求加载类型，默认查询。
          this.source.data.type = type || "SELECT";

          let o = this;
          // 对数据进行八爪鱼脚本过滤（前端配置的代码，因为后台只要有八爪鱼代码，就会在后台处理掉。）
          // 主要是解析#{param=xxx},#{control=xxx}的数据
          this.source.data.value = this.octopus.tools.parseScript(this.source.data.value);
          // // 八爪鱼数据源编辑页、列编辑页不执行替换语义
          // if (this.octopus.router.get().fullPath.indexOf("Engine/Datasource/Modify") == -1) {
          //   // 解析cell判斷是否存在默认值需要转义，此处主要针对前端代码设置绑定的转义。
          //   // 如果是后台配置，非前端代码，则这个函数是没有必要存在的。
          $(this.source.data.cellData).each(function (index, cell) {
            // 查询模式下，需要清空默认值，否则会有缓存。
            if (o.source.data.type == "SELECT") {
              cell.defaultValue = null;
            }
          });

          // 后面多级下拉框的实现逻辑就是遍历item的数据库配置，如果发现有联动属性，则追加动态监听，即可实现动态联动。
          // if (type != "SELECT") {
          //   console.log(JSON.stringify(this.source.data));
          // }

          // console.log("））））"+JSON.stringify(this.source.data.paramData));
          // 请求后台八爪鱼API框架。
          this.octopus.request.api.post("/Octopus/Engine/Item", this.source.data,
            // 处理回调事件，通过分析data.status.code的状态来实现。
            function (data) {
              if (data.status.code == 200) {
                // 加载之前，需要注意的是此时数据并不完整，只是刚从后台获取到。
                if (o.loadBefore) {
                  o.loadBefore(data);
                }

                // Item控件具备属性验证，这里追加validate属性，默认验证通过，控件初始化时会自动
                $(data.content.cellData).each(function (index, cell) {
                  cell.validate = true;
                  cell.model = "ITEM";
                  // if(cell.controlTypeID == 17) {
                  //   cell.defaultValue = (cell.defaultValue+"").replace(/-/g, "/");
                  // }

                  ////////////////////////  列关联数据处理，支持关联多个父级，但是mapping需要列不同 //////////////////////
                  if (cell.link) {
                    $(cell.link).each(function (linkIndex, linkData) {
                      // 查找当前关联列是否存在
                      let bindingData = $.grep(data.content.cellData, function (item) {
                        return item.name == linkData.binding;//筛选出当前数据列数据
                      });
                      // alert(JSON.stringify(linkColumn));
                      if (bindingData.length == 1) {
                        // 赋值
                        linkData.value = bindingData[0].defaultValue;
                      }
                    });
                  }
                });

                // 更新对象，前提是值不一致，如果一致则不更新，更新到最底层。
                // o.octopus.tools.updateObject(o.source.data, data.content);
                // 新增属性，需要使用$set，详细参考：https://blog.csdn.net/panyang01/article/details/76665448
                // console.log(JSON.stringify(data.content));

                o.$set(o.source, "data", data.content);

                if (o.source.data.type == "SELECT") {
                  if (o.loaded) {
                    o.loaded(data);
                  }
                } else if (o.source.data.type == "UPDATE" || o.source.data.type == "CREATE") {
                  if (o.saved) {
                    o.saved(data);
                  }
                  // 如果数据有更新回调，此处是和saved区分开，因为saved会导致保存按钮出现。
                  if (o.updated) {
                    o.updated(data);
                  }

                  // 记录创建 或 修改日志
                  o.octopus.request.api.logger(o.source.data.type == "UPDATE" ? "modify" : "create", data.content);
                }

                // 返回成功回调，主要为了解决在不通过 save 保存的情况下，如何获得成功后的回调。
                if (o.successed) {
                  o.successed(data);
                }
              } else {
                o.octopus.tips.error(data);
              }

              // 初始化link数据。
              o.loadLinkData();

            });
        }
      },
      // 初始化link数据
      loadLinkData: function () {
        let o = this;
        // 八爪鱼托管赋值
        $(this.source.data.cellData).each(function (index, cell) {
          if (cell.link) {
            // 循环link数据，并自动配置绑定的name。
            $(cell.link).each(function (linkIndex, linkData) {
              // 循环当前列，进行匹配。
              $(o.source.data.cellData).each(function (index_root, cell_root) {
                if (linkData.binding == cell_root.name) {
                  // console.warn(linkData.binding + "," + cell_root.name + "," + JSON.stringify(cell_root));
                  linkData.name = cell_root.displayName;
                }
              });
            });
          }
        });
      },
      // 完全彻底的刷新控件的方法。
      reload: function () {
        let o = this;
        this.status = false;
        this.$nextTick(function () {
          o.status = true;
          o.load();
        });
      },
      validate: function () {
        let o = this;
        let status = true;
        $(this.source.data.cellData).each(function (index, cell) {
          // 如果找到一个false就弹出不合法。
          if (!cell.validate) {
            status = false;
            o.octopus.tips.error("列：" + cell.displayName + " 数据不合法，请输入合规数据以后再试。");
            return status;
          }
        });
        // 验证回调事件，给开发者在保存前最后一次外部干预的机会。
        if (this.validated) {
          status = this.validated(status);
        }
        return status;
      },
      save: function () {
        // 此处执行数据轮询验证，就是查看validate是否都为true，因为主体验证都是有控件本身完成的。
        if (this.validate()) {
          // 执行更新或插入
          if (this.source.data.value) {
            this.load("UPDATE");
          } else {
            this.load("CREATE");
          }
        }
      },
      // 暂时没想到打印怎么实现。。
      // print: function (index) {
      //
      // },
      back: function () {
        // 如果存在则回调返回函数。
        if (this.backed) {
          this.backed();
        }
      },
      init: function () {
        this.initData();
      },
      // 初始化数据，主要是还原#param等预设参数，重新绑定。
      initData: function () {
        // console.log(this.source);
        // 是否需要缓存，Item组件默认是拒绝缓存，否则第二次进来就是上次的保存值，所有的初始化参数设置都会被覆盖，导致异常。
        this.source.cache = this.source.cache || false;
        // 保存整体控件，给后续引用方法，调用使用，极其方便。
        this.source.control = this;
        // data数据深拷贝用，默认null，只有当Cache为false时，会用到这个对象，用于在第一次加载时保存开发者配置的数据。
        // 以便于每次进入时像第一次进入一下进行解码、换算，绑定，渲染事件，
        this.source.dataExtend = this.source.dataExtend || null;

        // 如果禁止缓存，则在创建时拷贝原始source对象，组件注销时还原。
        if (!this.source.cache) {
          // 执行JQ浅拷贝（只复制顶层的非 object 元素）
          // var newObject = jQuery.extend({}, oldObject);
          // 执行深拷贝，也就拷贝data数据，因为这个是会变的，其他的cache、control等属性是组件内置，外围可配，但都是开发者定义的。
          this.dataExtend = $.extend(true, {}, this.source.data);
        }
      },
      // 组件停用时把data修正回原始数据，其他属性因为不会变，所以不用还原。
      destroyData: function () {
        // 如果禁用缓存，则停止组件时还原初始化Source配置数据源。
        if (!this.source.cache) {
          this.source.data = $.extend(true, {}, this.dataExtend);
        }
      },
      // 控件数据变更事件
      change: function (new_cell, old_cell) {
        let o = this;
        // 处理关联数据
        setLinkData(new_cell.name, new_cell.defaultValue);

        // 递归处理
        function setLinkData(linkName, linkValue) {
          // 列关联数据处理，支持关联多个父级，但是mapping需要列不同
          $(o.source.data.cellData).each(function (index, cell) {
            if (cell.link) {
              $(cell.link).each(function (linkIndex, linkData) {
                // 找到 link 相关的字段
                if (linkData.binding == linkName) {
                  // 赋值
                  linkData.value = linkValue;
                  cell.defaultValue = null;
                  // 判断当前控件是否是其他控件的父亲，如果是则同步处理。
                  setLinkData(cell.name, cell.defaultValue);
                }
              });
            }
          });
        }

        if (this.changed) {
          this.changed(new_cell, old_cell);
        }
      },
      event: function (callback) {
        callback(this.source.data);
      }
    },
    watch: {
      // 监听器需要记住所有需要替换的控件值？可是如果被替换了，还需要按照格式替换么。
      // 'source.cache': function (newv, oldv){
      //   alert('11111');
      // },
      // // 只要data.value发生变化，则必须重新渲染控件，因为该值是主键，一切的根本。
      // 'source.data.value': function (newv, oldv) {
      //   // 禁止不等于、为空、或者第一次加载带解析符号 && oldv.indexOf("#") == -1
      //   if (newv != oldv && !this.octopus.tools.IsNullOrEmpty(newv)) {
      //     // 触发数据改变时间，回调函数。
      //     if (this.changed) {
      //       this.changed();
      //     }
      //   }
      // },
      // 深度监听（观察者模式），可监听由父组件改变的对象状态
      // 'source.data.cellData': {
      //   handler(newValue, oldValue) {
      //     //父组件param对象改变会触发此函数
      //     console.log("=====new,deep,");
      //   },
      //   deep: true
      // },
      // // 监听计算属性的变化
      // changeCell: function (newval, oldval) {
      //   console.log("changeCell:\nOLD: " + JSON.stringify(oldval) + "\n\nNEW: " + JSON.stringify(newval));
      // }
    },
    props: ["source", "loadBefore", "loaded", "validated", "saved", "successed", "updated", "changed", "backed"]
  }

</script>

/*  scoped的意思是这里的样式只对当前页面有效不会影响其他页面，还有可以设置lang="scss"就是支持css预编译，也就是支持sass或者less  */
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
  h1, h2 {
    font-weight: normal;
  }
</style>
